#ifndef GAMEKIT_SHAREDPOINTER_H
#define GAMEKIT_SHAREDPOINTER_H

#include "../gamekit.h"

namespace gamekit
{
/*------------------------------------------------------------------------------------*/
/* gamekit::RefCountWrapper<T> */
/*------------------------------------------------------------------------------------*/

	template<typename T>
	class RefCountWrapper
	{
	private:
		T* m_t;
		uint m_refCount;
	public:
		RefCountWrapper(T* t);
		~RefCountWrapper();

		void increase(void);
		void decrease(void);

		T* t(void) const;

		bool isOrphan(void) const;
	};

/*------------------------------------------------------------------------------------*/
/* gamekit::SharedPointer<T> */
/*------------------------------------------------------------------------------------*/
	template<typename T>
	class GK_API SharedPointer
	{
	private:
		RefCountWrapper<T>* m_wrapper;

	public:
		SharedPointer();
		SharedPointer(T* t);
		SharedPointer(const SharedPointer<T>& tPtr);

		~SharedPointer();

		SharedPointer<T>& operator =(T* t);
		SharedPointer<T>& operator =(const SharedPointer<T>& tPtr);

		T* operator ->() const;
		bool operator !() const;
		operator bool() const;

		inline friend bool operator==(const SharedPointer<T>& sp, const T* t)
		{
			check();
			return sp.m_wrapper->t() == t;
		}

		inline friend bool operator==(const T* t, const SharedPointer<T>& sp)
		{
			check();
			return t == sp.m_wrapper->t();
		}

		inline friend bool operator!=(const SharedPointer<T>& sp, const T* t)
		{
			check();
			return sp.m_wrapper->t() != t;
		}

		inline friend bool operator!=(const T* t, const SharedPointer<T>& sp)
		{
			check();
			return t != sp.m_wrapper->t();
		}

		template<typename U>
		inline friend bool operator==(const SharedPointer<T>& sp, const U* t)
		{
			check();
			return sp.m_wrapper->t() == t;
		}

		template<typename U>
		inline friend bool operator!=(const U* t, const SharedPointer<T>& sp)
		{
			check();
			return t != sp.m_wrapper->t();
		}

		template<typename U>
		inline friend bool operator==(const SharedPointer<T>& sp, const SharedPointer<U>& su)
		{
			check();
			return sp.m_wrapper->t() == su.m_wrapper->t();
		}

		template<typename U>
		inline friend bool operator!=(const SharedPointer<U>& su, const SharedPointer<T>& sp)
		{
			check();
			return su.m_wrapper->t() != sp.m_wrapper->t();
		}

	private:
		void release(void);
		void check(void) const;
	};

/*====================================================================================*/
/* gamekit::RefCountWrapper<T> Implementation */
/*====================================================================================*/

	template<typename T>
	RefCountWrapper<T>::RefCountWrapper(T* t)
		: m_t(t),
		m_refCount(0)
	{

	}

	template<typename T>
	RefCountWrapper<T>::~RefCountWrapper()
	{
		delete m_t;
	}

	template<typename T>
	void RefCountWrapper<T>::increase(void)
	{
		m_refCount++;
	}

	template<typename T>
	void RefCountWrapper<T>::decrease(void)
	{
		m_refCount--;
	}

	template<typename T>
	bool RefCountWrapper<T>::isOrphan(void) const
	{
		return m_refCount == 0;
	}

	template<typename T>
	T* RefCountWrapper<T>::t(void) const
	{
		if(m_refCount == 0)
		{
			return NULL;
		}

		return m_t;
	}

/*====================================================================================*/
/* gamekit::SharedPointer<T> Implementation */
/*====================================================================================*/

	template<typename T>
	SharedPointer<T>::SharedPointer() :
		m_wrapper(NULL)
	{
	}

	template<typename T>
	SharedPointer<T>::SharedPointer(T* t)
		: m_wrapper(NULL)
	{
		m_wrapper = new RefCountWrapper<T>(t);
		m_wrapper->increase();
	}

	template<typename T>
	SharedPointer<T>::SharedPointer(const SharedPointer<T>& tPtr)
		: m_wrapper(NULL)
	{
		if(tPtr)
		{
			m_wrapper = tPtr.m_wrapper;
			m_wrapper->increase();
		}
	}

	template<typename T>
	SharedPointer<T>::~SharedPointer()
	{
		release();
	}

	template<typename T>
	SharedPointer<T>& SharedPointer<T>::operator =(T* t)
	{
		release();

		m_wrapper = new RefCountWrapper<T>(t);
		m_wrapper->increase();

		return *this;
	}

	template<typename T>
	SharedPointer<T>& SharedPointer<T>::operator =(const SharedPointer<T>& tPtr)
	{
		release();

		m_wrapper = tPtr.m_wrapper;
		m_wrapper->increase();

		return *this;
	}

	template<typename T>
	T* SharedPointer<T>::operator ->() const
	{
		check();

		return m_wrapper->t();
	}

	template<typename T>
	bool SharedPointer<T>::operator !() const
	{
		return m_wrapper == NULL;
	}

	template<typename T>
	SharedPointer<T>::operator bool() const
	{
		return m_wrapper != NULL;
	}

	template<typename T>
	void SharedPointer<T>::release(void)
	{
		if(m_wrapper == NULL)
		{
			return;
		}

		m_wrapper->decrease();

		if(m_wrapper->isOrphan())
		{
			delete m_wrapper;
			m_wrapper = NULL;
		}
	}

	template<typename T>
	void SharedPointer<T>::check(void) const
	{
		if(m_wrapper == NULL)
		{
			throw std::exception();
		}
	}

}

#endif
